package fr.labri.buganalyzer.datamining;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Session;

import fr.labri.buganalyzer.model.Issue;
import fr.labri.buganalyzer.writers.AbstractAdapter;
import fr.labri.buganalyzer.writers.H2Adapter;

public class ARFFgenerator {

	private AbstractAdapter adapter;
	private String output;
	public static final int MAX_INSTANCES = 3000;

	public ARFFgenerator(String database, String outputFolder) {
		adapter = new H2Adapter(database);
		this.output = outputFolder;
	}

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(final String[] args) throws IOException {
		String dataBase = "resources/testData/ProductionIssuesDB_apache";
		Path path = Paths.get("resources/testData/apache.arff");
		ARFFgenerator generator = new ARFFgenerator(dataBase, path.toString());
		Session session = generator.getAdapter().getSession();
		
		String query = "from Issue";
		
		List<Issue> resutls = getResults(query, session);

		String header = generator.getHeader(resutls);

		StringBuilder data = generator.getData(resutls);

		generator.writeARFF(path, header, data);
	}

	private void writeARFF(Path path, String header, StringBuilder data)
			throws IOException {
		// Store results in ARFF files
		Path file = Files.createFile(path);
		try (BufferedWriter writer = Files.newBufferedWriter(file,
				StandardCharsets.UTF_8, StandardOpenOption.WRITE)) {
			writer.write(header);
			writer.write(data.toString());
		}
	}

	private StringBuilder getData(List<Issue> issues) {
		StringBuilder sb = new StringBuilder();
		for (Issue issue : issues) {

			String assignee = "void";
			if (issue.getAssignee() != null)
				assignee = issue.getAssignee().getAlias();
			String reporter = "void";
			if (issue.getReporter() != null)
				reporter = issue.getReporter().getAlias();
			String product = "void";
			if (issue.getProduct() != null)
				product = issue.getProduct().replaceAll(" ", "_");
			String component = "void";
			if (issue.getComponent() != null)
				component = issue.getComponent().replaceAll(" ", "_");
			sb.append(issue.getNumber() + " " + product + " " + component + " "
					+ reporter + " " + assignee);
			sb.append("\n");
		}
		return sb;
	}

	@SuppressWarnings("unchecked")
	private static List<Issue> getResults(String query, Session session) {
		
		List<Issue> resutls = session.createQuery(query).setMaxResults(MAX_INSTANCES).list();
		return resutls;
	}

	/**
	 * Define ARFF file header and attributes
	 */
	private String getHeader(List<Issue> results) {
		//get nominal attributes : product, components, assignees, reporters
		Set<String> products = new HashSet<String>();
		Set<String> components = new HashSet<String>();
		Set<String> assignees = new HashSet<String>();
		Set<String> reporters = new HashSet<String>();
		
		for (Issue i : results) {
			if (i.getProduct() != null) products.add(i.getProduct().replaceAll(" ", "_"));
			if (i.getComponent() != null) components.add(i.getComponent().replaceAll(" ", "_"));
			if (i.getAssignee() != null) assignees.add(i.getAssignee().getAlias());
			if (i.getReporter() != null) reporters.add(i.getReporter().getAlias());
		}
		
		String prods = getInlineListe(products);
		String comps = getInlineListe(components);
		String assigs = getInlineListe(assignees);
		String reports = getInlineListe(reporters);
		
		String header = "% ARFF file for the issues data for the apache product% \n\n"
				+ "@relation issue"
				+ "\n\n"
				+ "@attribute number numeric\n"
				+ "@attribute product " + prods + "\n\n"
				+ "@attribute component " + comps + "\n\n"
				+ "@attribute reporter " + reports + "\n\n"
				+ "@attribute assignee " + assigs + "\n\n"
				+ "\n\n"
				+ "@data\n\n"
				+ "%\n%  " + results.size() + "\n%\n";
		return header;
	}

	private String getInlineListe(Set<String> set) {
		String result = "{";
		for (String s : set) {
			result += s + ", ";
		}
		result += "void}";
		return result;
	}

	public final AbstractAdapter getAdapter() {
		return adapter;
	}

}
